﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.Collections.Generic;
using ConfigGen.ConsoleApp.Core.CommandLineOptionCore;
using NUnit.Framework;

namespace ConfigGen.ConsoleApp.Core.Tests.CommandLineOptionCore
{
    [TestFixture]
    public class CommandLineOptionFactoryTests
    {
        [Test]
        public void Ctor001_NullArgThrows()
        {
            Assert.Throws<ArgumentNullException>(() => new TestCommandLineOptionFactory(null));
        }

        [Test]
        public void Ctor002_CollisionOnShortFormThrows()
        {
            var exception = Assert.Throws<FactoryLoadException>(
                () =>
                new TestCommandLineOptionFactory(new[]
                                              {
                                                  new TestCommandLineOption {ShortName = "a", LongName = "aa"},
                                                  new TestCommandLineOption {ShortName = "a", LongName = "bb"},
                                              }));

            Assert.IsTrue(exception.Message.Contains("short"), "Exception message should have contained the word 'short'");
        }

        [Test]
        public void Ctor003_CollisionOnLongFormThrows()
        {
            var exception = Assert.Throws<FactoryLoadException>(
                () =>
                new TestCommandLineOptionFactory(new[]
                                              {
                                                  new TestCommandLineOption {ShortName = "a", LongName = "aa"},
                                                  new TestCommandLineOption {ShortName = "b", LongName = "aa"},
                                              }));

            Assert.IsTrue(exception.Message.Contains("long"), "Exception message should have contained the word 'long'");
        }

        [Test]
        public void Ctor004_HandlerHasNullLongAndShortNameThrows()
        {
            var exception = Assert.Throws<FactoryLoadException>(
                () =>
                new TestCommandLineOptionFactory(new[]
                                              {
                                                  new TestCommandLineOption {ShortName = string.Empty, LongName = string.Empty},
                                              }));

            Assert.IsTrue(exception.Message.Contains("neither"), "Exception message should have contained the word 'neither'");
        }

        [Test]
        public void Ctor005_HandlerWithLongAndShortNameLoads()
        {

            var factory = new TestCommandLineOptionFactory(new[]
                                          {
                                              new TestCommandLineOption {ShortName = "a", LongName = "aa"}
                                          });
        }

        [Test]
        public void Ctor006_HandlerWithLongNameOnlyLoads()
        {

            var factory = new TestCommandLineOptionFactory(new[]
                                          {
                                              new TestCommandLineOption {ShortName = "a"}
                                          });
        }

        [Test]
        public void Ctor007_HandlerWithShortNameOnlyLoads()
        {

            var factory = new TestCommandLineOptionFactory(new[]
                                          {
                                              new TestCommandLineOption {LongName = "aa"}
                                          });
        }

        [Test]
        public void GetByShortName001_ArgNullThrows()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });
            Assert.Throws<ArgumentNullException>(() => factoryUnderTest.GetByShortName(null));
        }

        [Test]
        public void GetByShortName002_ArgEmptyThrows()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });
            Assert.Throws<ArgumentException>(() => factoryUnderTest.GetByShortName(""));
        }

        [Test]
        public void GetByShortName004_Exists()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory( new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });

            var handler = factoryUnderTest.GetByShortName("b");
            Assert.AreEqual("b", handler.ShortName, "Incorrect handler returned");
        }

        [Test]
        public void GetByShortName003_DoesNotExist()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });

            var exception = Assert.Throws<UnrecognisedCommandLineOptionException>(() => factoryUnderTest.GetByShortName("d"));
            Assert.IsNull(exception.LongName, "No long name expected in exception");
            Assert.AreEqual("d", exception.ShortName, "Incorrect short name");
        }

        [Test]
        public void GetByLongName001_ArgNullThrows()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });
            Assert.Throws<ArgumentNullException>(() => factoryUnderTest.GetByLongName(null));
        }

        [Test]
        public void GetByLongName002_ArgEmptyThrows()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });
            Assert.Throws<ArgumentException>(() => factoryUnderTest.GetByLongName(""));
        }

        [Test]
        public void GetByLongName003_Exists()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });

            var handler = factoryUnderTest.GetByLongName("bb");
            Assert.AreEqual("b", handler.ShortName, "Incorrect handler returned");
        }

        [Test]
        public void GetByLongName004_DoesNotExist()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });

            var exception = Assert.Throws<UnrecognisedCommandLineOptionException>(() => factoryUnderTest.GetByLongName("dd"));
            Assert.IsNull(exception.ShortName, "No short name expected in exception");
            Assert.AreEqual("dd", exception.LongName, "Incorrect long name");
        }

        [Test]
        public void LoadedHandlers001()
        {
            var factoryUnderTest = new TestCommandLineOptionFactory(new[]
                    {
                        new TestCommandLineOption { ShortName = "a", LongName = "aa"},
                        new TestCommandLineOption { ShortName = "b", LongName = "bb"},
                        new TestCommandLineOption { ShortName = "c", LongName = "cc"},
                    });

            var loadedHandlers = factoryUnderTest.LoadedHandlers;
            Assert.AreEqual(3, loadedHandlers.Count, "Incorrect handler count");
            Assert.AreEqual("a", loadedHandlers[0].ShortName, "Incorrect item 0 in loaded handlers collection");
            Assert.AreEqual("b", loadedHandlers[1].ShortName, "Incorrect item 1 in loaded handlers collection");
            Assert.AreEqual("c", loadedHandlers[2].ShortName, "Incorrect item 2 in loaded handlers collection");
        }

        public class TestCommandLineOption : ICommandLineOption<TestSettings>
        {
            #region ICommandLineOption<TestSettings> Members

            public string ShortName { get; set; }
            public string LongName { get; set; }

            public string ArgsUsage
            {
                get { throw new NotImplementedException(); }
            }

            public string HelpText
            {
                get { throw new NotImplementedException(); }
            }

            public void ApplyOptionToSettings(TestSettings settings)
            {
                throw new NotImplementedException();
            }

            public void ConsumeArgumentsFromQueue(Queue<string> args)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        public class TestCommandLineOptionFactory : CommandLineOptionFactory<TestSettings>
        {
            public TestCommandLineOptionFactory(IEnumerable<ICommandLineOption<TestSettings>> handlers)
            {
                if (handlers == null)
                {
                    LoadConsoleArgHandler(null);
                }
                else
                {
                    foreach (var handler in handlers)
                    {
                        LoadConsoleArgHandler(handler);
                    }
                }
            }
        }
    }
}
